import { registry } from '@/registry'
import { fetchAndWriteIcons } from '@saas-js/iconify'
import {
  Registry,
  RegistryItem,
  registryItemSchema,
  registryItemTypeSchema,
  registrySchema,
} from '@saas-ui/registry/schema'
import { Eta } from 'eta'
import { existsSync, promises as fs } from 'node:fs'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { cwd } from 'node:process'
import { pathToFileURL } from 'node:url'
import { rimraf } from 'rimraf'
import { Project, ScriptKind, SyntaxKind } from 'ts-morph'
import { z } from 'zod'

import { iconLibraries, icons } from '../registry/registry-icons'
import { styles } from '../registry/registry-styles'

const baseColors: Array<any> = []
const colorMapping: Array<any> = []
const colors: Array<any> = []

const REGISTRY_PATH = path.join(process.cwd(), 'public/r')

const REGISTRY_INDEX_WHITELIST: z.infer<typeof registryItemTypeSchema>[] = [
  'registry:ui',
  'registry:lib',
  'registry:hook',
  'registry:theme',
  'registry:block',
  'registry:icon',
]

const project = new Project({
  compilerOptions: {},
})

async function createTempSourceFile(filename: string) {
  const dir = await fs.mkdtemp(path.join(tmpdir(), 'sui-'))
  return path.join(dir, filename)
}

async function importComponents(args: { path: string }) {
  const collection: Array<RegistryItem> = []

  async function findAndImportComponentConfigs(dir: string) {
    const entries = await fs.readdir(dir, { withFileTypes: true })

    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name)

      if (entry.isDirectory()) {
        await findAndImportComponentConfigs(fullPath)
      } else if (entry.isFile() && entry.name === 'component.config.ts') {
        try {
          // Dynamically import the component.config.ts file
          // Convert Windows paths to file:// URLs for ESM compatibility
          const module = await import(pathToFileURL(fullPath).href)

          // Add the imported object to the collection
          if (module.default) {
            collection.push(module.default)
          } else {
            console.warn(`No default export found in ${fullPath}`)
          }
        } catch (error) {
          console.error(`Error importing ${fullPath}:`, error)
        }
      }
    }
  }

  await findAndImportComponentConfigs(args.path)

  return collection
}

// ----------------------------------------------------------------------------
// Build __registry__/index.tsx.
// ----------------------------------------------------------------------------
async function buildRegistry(registry: Registry) {
  let index = `// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"

export const Index: Record<string, any> = {
`

  for (const style of styles) {
    index += `  "${style.name}": {`

    // Build style index.
    for (const item of registry.items) {
      const resolveFiles = item.files?.map(
        (file) => `../${typeof file === 'string' ? file : file.path}`,
      )

      if (!resolveFiles) {
        continue
      }

      const type = item.type.split(':')[1]
      let sourceFilename = ''

      let chunks: any = []
      if (item.type === 'registry:block') {
        const file = resolveFiles[0]
        const filename = path.basename(file)
        const raw = await fs.readFile(file, 'utf8')
        const tempFile = await createTempSourceFile(filename)
        const sourceFile = project.createSourceFile(tempFile, raw, {
          scriptKind: ScriptKind.TSX,
        })

        // Find all imports.
        const imports = new Map<
          string,
          {
            module: string
            text: string
            isDefault?: boolean
          }
        >()
        sourceFile.getImportDeclarations().forEach((node) => {
          const module = node.getModuleSpecifier().getLiteralValue()
          node.getNamedImports().forEach((item) => {
            imports.set(item.getText(), {
              module,
              text: node.getText(),
            })
          })

          const defaultImport = node.getDefaultImport()
          if (defaultImport) {
            imports.set(defaultImport.getText(), {
              module,
              text: defaultImport.getText(),
              isDefault: true,
            })
          }
        })

        // Find all opening tags with x-chunk attribute.
        const components = sourceFile
          .getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
          .filter((node) => {
            return node.getAttribute('x-chunk') !== undefined
          })

        chunks = await Promise.all(
          components.map(async (component, index) => {
            const chunkName = `${item.name}-chunk-${index}`

            // Get the value of x-chunk attribute.
            const attr = component
              .getAttributeOrThrow('x-chunk')
              .asKindOrThrow(SyntaxKind.JsxAttribute)

            const description = attr
              .getInitializerOrThrow()
              .asKindOrThrow(SyntaxKind.StringLiteral)
              .getLiteralValue()

            // Delete the x-chunk attribute.
            attr.remove()

            // Add a new attribute to the component.
            component.addAttribute({
              name: 'x-chunk',
              initializer: `"${chunkName}"`,
            })

            // Get the value of x-chunk-container attribute.
            const containerAttr = component
              .getAttribute('x-chunk-container')
              ?.asKindOrThrow(SyntaxKind.JsxAttribute)

            const containerClassName = containerAttr
              ?.getInitializer()
              ?.asKindOrThrow(SyntaxKind.StringLiteral)
              .getLiteralValue()

            containerAttr?.remove()

            const parentJsxElement = component.getParentIfKindOrThrow(
              SyntaxKind.JsxElement,
            )

            // Find all opening tags on component.
            const children = parentJsxElement
              .getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
              .map((node) => {
                return node.getTagNameNode().getText()
              })
              .concat(
                parentJsxElement
                  .getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)
                  .map((node) => {
                    return node.getTagNameNode().getText()
                  }),
              )

            const componentImports = new Map<
              string,
              string | string[] | Set<string>
            >()
            children.forEach((child) => {
              const importLine = imports.get(child)
              if (importLine) {
                const imports = componentImports.get(importLine.module) || []

                const newImports = importLine.isDefault
                  ? importLine.text
                  : new Set([...imports, child])

                componentImports.set(
                  importLine.module,
                  importLine?.isDefault ? newImports : Array.from(newImports),
                )
              }
            })

            const componnetImportLines = Array.from(
              componentImports.keys(),
            ).map((key) => {
              const values = componentImports.get(key)
              const specifier = Array.isArray(values)
                ? `{${values.join(',')}}`
                : values

              return `import ${specifier} from "${key}"`
            })

            const code = `
            'use client'

            ${componnetImportLines.join('\n')}

            export default function Component() {
              return (${parentJsxElement.getText()})
            }`

            const targetFile = file.replace(item.name, `${chunkName}`)
            const targetFilePath = path.join(
              cwd(),
              `registry/${style.name}/${type}/${chunkName}.tsx`,
            )

            // Write component file.
            rimraf.sync(targetFilePath)
            await fs.writeFile(targetFilePath, code, 'utf8')

            return {
              name: chunkName,
              description,
              component: `React.lazy(() => import("@/registry/${style.name}/${type}/${chunkName}")),`,
              file: targetFile,
              container: {
                className: containerClassName,
              },
            }
          }),
        )

        // Write the source file for blocks only.
        sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx`

        if (item.files) {
          const files = item.files.map((file) =>
            typeof file === 'string'
              ? { type: 'registry:page', path: file }
              : file,
          )
          if (files?.length) {
            sourceFilename = `__registry__/${style.name}/${files[0].path}`
          }
        }

        const sourcePath = path.join(process.cwd(), sourceFilename)
        if (!existsSync(sourcePath)) {
          await fs.mkdir(sourcePath, { recursive: true })
        }

        rimraf.sync(sourcePath)
        await fs.writeFile(sourcePath, sourceFile.getText())
      }

      let componentPath = `@/registry/${style.name}/${type}/${item.name}`

      if (item.files) {
        const files = item.files.map((file) =>
          typeof file === 'string'
            ? { type: 'registry:page', path: file }
            : file,
        )
        if (files?.length) {
          componentPath = `@/registry/${style.name}/${files[0].path}`
        }
      }

      index += `
    "${item.name}": {
      name: "${item.name}",
      type: "${item.type}",
      registryDependencies: ${JSON.stringify(item.registryDependencies)},
      files: [${resolveFiles.map((file) => `"${file}"`)}],
      component: React.lazy(() => import("${componentPath}")),
      source: "${sourceFilename}",
      categories: ${JSON.stringify(item.categories)},
      chunks: [${chunks.map(
        (chunk: any) => `{
        name: "${chunk.name}",
        description: "${chunk.description ?? 'No description'}",
        component: ${chunk.component}
        file: "${chunk.file}",
        container: {
          className: "${chunk.container.className}"
        }
      }`,
      )}]
    },`
    }

    index += `
  },`
  }

  index += `
}
`

  // ----------------------------------------------------------------------------
  // Build registry/index.json.
  // ----------------------------------------------------------------------------
  // Import icon registry to include in index
  const { icons: iconRegistry } = await import(
    '../registry/registry-icons-generated'
  )

  const items = [
    ...registry.items
      .filter((item) => ['registry:ui', 'registry:block'].includes(item.type))
      .map((item) => {
        return {
          ...item,
          files: item.files?.map((_file) => {
            const file =
              typeof _file === 'string'
                ? {
                    path: _file,
                    type: item.type,
                  }
                : _file

            return file
          }),
        }
      }),
    ...iconRegistry.map((icon: any) => ({
      name: icon.name,
      type: icon.type,
      files: icon.files,
      meta: icon.meta,
    })),
  ]

  const registryJson = JSON.stringify(items, null, 2)
  rimraf.sync(path.join(REGISTRY_PATH, 'index.json'))
  await fs.writeFile(
    path.join(REGISTRY_PATH, 'index.json'),
    registryJson,
    'utf8',
  )

  // Write style index.
  rimraf.sync(path.join(process.cwd(), '__registry__/index.tsx'))
  await fs.writeFile(path.join(process.cwd(), '__registry__/index.tsx'), index)
}

// ----------------------------------------------------------------------------
// Build registry/styles/[style]/[name].json.
// ----------------------------------------------------------------------------
async function buildStyles(registry: Registry) {
  for (const style of styles) {
    const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)

    // Create directory if it doesn't exist.
    if (!existsSync(targetPath)) {
      await fs.mkdir(targetPath, { recursive: true })
    }

    for (const item of registry.items) {
      if (!REGISTRY_INDEX_WHITELIST.includes(item.type)) {
        continue
      }

      let files
      if (item.files) {
        files = await Promise.all(
          item.files.map(async (_file) => {
            const file =
              typeof _file === 'string'
                ? {
                    path: _file,
                    type: item.type,
                    content: '',
                    target: '',
                  }
                : _file

            const content = await fs.readFile(
              path.join(process.cwd(), 'registry', style.name, file.path),
              // path.join(process.cwd(), '..', file.path),
              'utf8',
            )

            const tempFile = await createTempSourceFile(file.path)
            const sourceFile = project.createSourceFile(tempFile, content, {
              scriptKind: ScriptKind.TSX,
            })

            sourceFile.getVariableDeclaration('canvas')?.remove()

            return {
              path: file.path,
              type: file.type,
              content: sourceFile.getText(),
              target: file.target,
            }
          }),
        )
      }

      const payload = registryItemSchema
        .omit({
          source: true,
          categories: true,
          chunks: true,
        })
        .safeParse({
          ...item,
          files,
        })

      if (payload.success) {
        await fs.writeFile(
          path.join(targetPath, `${item.name}.json`),
          JSON.stringify(payload.data, null, 2),
          'utf8',
        )
      }
    }
  }

  // ----------------------------------------------------------------------------
  // Build registry/styles/index.json.
  // ----------------------------------------------------------------------------
  const stylesJson = JSON.stringify(styles, null, 2)
  await fs.writeFile(
    path.join(REGISTRY_PATH, 'styles/index.json'),
    stylesJson,
    'utf8',
  )
}

// ----------------------------------------------------------------------------
// Build registry/styles/[name]/index.json.
// ----------------------------------------------------------------------------
async function buildStylesIndex() {
  for (const style of styles) {
    const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)

    const dependencies = [
      // 'tailwindcss-animate',
      // 'class-variance-authority',
      // 'lucide-react',
    ] as Array<string>

    const payload: RegistryItem = {
      name: style.name,
      type: 'registry:style',
      dependencies,
      registryDependencies: [],
      cssVars: {},
      files: [],
    }

    await fs.writeFile(
      path.join(targetPath, 'index.json'),
      JSON.stringify(payload, null, 2),
      'utf8',
    )
  }
}

// ----------------------------------------------------------------------------
// Build registry/styles/[style]/[icon-name].json for icon components
// ----------------------------------------------------------------------------
async function buildIconsRegistry() {
  // Import generated icon registry configuration
  const { icons: iconRegistry } = await import(
    '../registry/registry-icons-generated'
  )

  for (const style of styles) {
    const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)

    for (const icon of iconRegistry) {
      const iconPath = path.join(
        process.cwd(),
        'registry',
        style.name,
        icon.files[0].path,
      )

      // Check if icon file exists
      if (!existsSync(iconPath)) {
        console.warn(`Icon file not found: ${iconPath}`)
        continue
      }

      const content = await fs.readFile(iconPath, 'utf8')

      const payload = {
        name: icon.name,
        type: icon.type,
        files: [
          {
            path: icon.files[0].path,
            type: icon.files[0].type,
            content,
          },
        ],
        meta: icon.meta,
      }

      await fs.writeFile(
        path.join(targetPath, `${icon.name}.json`),
        JSON.stringify(payload, null, 2),
        'utf8',
      )
    }
  }
}

// ----------------------------------------------------------------------------
// Build registry/icons/index.json.
// ----------------------------------------------------------------------------
async function buildIcons() {
  const iconsTargetPath = path.join(REGISTRY_PATH, 'icons')
  rimraf.sync(iconsTargetPath)
  if (!existsSync(iconsTargetPath)) {
    await fs.mkdir(iconsTargetPath, { recursive: true })
  }

  const iconsData = icons

  await fs.writeFile(
    path.join(iconsTargetPath, 'index.json'),
    JSON.stringify(iconsData, null, 2),
    'utf8',
  )
}

// ----------------------------------------------------------------------------
// Build __registry__/icons.tsx and generate icon component files
// ----------------------------------------------------------------------------
async function buildRegistryIcons() {
  // Generate icon component files using @saas-js/iconify
  const iconRegistryPath = '/registry/default/icons'

  // Ensure icons directory exists
  const fullIconPath = path.join(process.cwd(), 'registry/default/icons')
  if (!existsSync(fullIconPath)) {
    await fs.mkdir(fullIconPath, { recursive: true })
  }

  // Group icons by library and create aliases mapping
  const iconsByLibrary: Record<
    string,
    {
      iconNames: string[]
      aliases: Record<string, string>
    }
  > = {}

  for (const [iconName, iconSets] of Object.entries(icons)) {
    // Use lucide as default for now
    const library = 'lucide'
    if (!iconsByLibrary[library]) {
      iconsByLibrary[library] = {
        iconNames: [],
        aliases: {},
      }
    }

    // Get the actual icon name from the library
    const actualIconName = iconSets[library as keyof typeof iconSets]
    if (!actualIconName) {
      console.warn(`Icon ${iconName} not found in ${library}`)
      continue
    }

    // Convert PascalCase to kebab-case for API and output
    const apiIconName = actualIconName
      .replace(/([a-z])([A-Z])/g, '$1-$2')
      .toLowerCase()
    const outputName = iconName
      .replace(/([a-z])([A-Z])/g, '$1-$2')
      .toLowerCase()

    iconsByLibrary[library].iconNames.push(apiIconName)

    // If the output name differs from API name, add to aliases
    if (outputName !== apiIconName) {
      iconsByLibrary[library].aliases[apiIconName] = outputName
    }
  }

  // Fetch and write icons for each library
  const generatedIcons: string[] = []
  for (const [library, { iconNames, aliases }] of Object.entries(
    iconsByLibrary,
  )) {
    console.log(`Generating ${iconNames.length} icons from ${library}...`)

    const generated = await fetchAndWriteIcons({
      iconSet: library,
      iconNames,
      outputDir: iconRegistryPath,
      aliases,
      shouldOverwrite: async () => true,
    })

    generatedIcons.push(...generated)
  }

  // Generate index.ts for re-exporting all icons
  const iconIndexPath = path.join(fullIconPath, 'index.ts')
  const iconExports = generatedIcons
    .map((iconName) => {
      return `export * from './${iconName}-icon'`
    })
    .join('\n')

  await fs.writeFile(iconIndexPath, iconExports + '\n', 'utf8')
  console.log(`Generated index.ts with ${generatedIcons.length} icon exports`)

  // Build the index file for lazy loading
  let index = `// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"

export const Icons = {
`

  for (const [icon, libraries] of Object.entries(icons)) {
    index += `  "${icon}": {`
    for (const [library, componentName] of Object.entries(libraries)) {
      // @ts-ignore
      const packageName = iconLibraries[library].package
      if (packageName) {
        index += `
  ${library}: React.lazy(() => import("${packageName}").then(mod => ({
    default: mod.${componentName}
  }))),`
      }
    }
    index += `
},`
  }

  index += `
}
`

  // Write style index.
  rimraf.sync(path.join(process.cwd(), '__registry__/icons.tsx'))
  await fs.writeFile(
    path.join(process.cwd(), '__registry__/icons.tsx'),
    index,
    'utf8',
  )
}

// ----------------------------------------------------------------------------
// Build registry/colors/index.json.
// ----------------------------------------------------------------------------
async function buildThemes() {
  const colorsTargetPath = path.join(REGISTRY_PATH, 'colors')
  rimraf.sync(colorsTargetPath)
  if (!existsSync(colorsTargetPath)) {
    await fs.mkdir(colorsTargetPath, { recursive: true })
  }

  const colorsData: Record<string, any> = {}
  for (const [color, value] of Object.entries(colors)) {
    if (typeof value === 'string') {
      colorsData[color] = value
      continue
    }

    if (Array.isArray(value)) {
      colorsData[color] = value.map((item) => ({
        ...item,
        rgbChannel: item.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'),
        hslChannel: item.hsl.replace(
          /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/,
          '$1 $2 $3',
        ),
      }))
      continue
    }

    if (typeof value === 'object') {
      colorsData[color] = {
        ...value,
        rgbChannel: value.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'),
        hslChannel: value.hsl.replace(
          /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/,
          '$1 $2 $3',
        ),
      }
      continue
    }
  }

  await fs.writeFile(
    path.join(colorsTargetPath, 'index.json'),
    JSON.stringify(colorsData, null, 2),
    'utf8',
  )

  // ----------------------------------------------------------------------------
  // Build registry/colors/[base].json.
  // ----------------------------------------------------------------------------
  const BASE_STYLES = `@tailwind base;
@tailwind components;
@tailwind utilities;
  `

  const BASE_STYLES_WITH_VARIABLES = `@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: <%- colors.light["background"] %>;
    --foreground: <%- colors.light["foreground"] %>;
    --card: <%- colors.light["card"] %>;
    --card-foreground: <%- colors.light["card-foreground"] %>;
    --popover: <%- colors.light["popover"] %>;
    --popover-foreground: <%- colors.light["popover-foreground"] %>;
    --primary: <%- colors.light["primary"] %>;
    --primary-foreground: <%- colors.light["primary-foreground"] %>;
    --secondary: <%- colors.light["secondary"] %>;
    --secondary-foreground: <%- colors.light["secondary-foreground"] %>;
    --muted: <%- colors.light["muted"] %>;
    --muted-foreground: <%- colors.light["muted-foreground"] %>;
    --accent: <%- colors.light["accent"] %>;
    --accent-foreground: <%- colors.light["accent-foreground"] %>;
    --destructive: <%- colors.light["destructive"] %>;
    --destructive-foreground: <%- colors.light["destructive-foreground"] %>;
    --border: <%- colors.light["border"] %>;
    --input: <%- colors.light["input"] %>;
    --ring: <%- colors.light["ring"] %>;
    --radius: 0.5rem;
    --chart-1: <%- colors.light["chart-1"] %>;
    --chart-2: <%- colors.light["chart-2"] %>;
    --chart-3: <%- colors.light["chart-3"] %>;
    --chart-4: <%- colors.light["chart-4"] %>;
    --chart-5: <%- colors.light["chart-5"] %>;
  }

  .dark {
    --background: <%- colors.dark["background"] %>;
    --foreground: <%- colors.dark["foreground"] %>;
    --card: <%- colors.dark["card"] %>;
    --card-foreground: <%- colors.dark["card-foreground"] %>;
    --popover: <%- colors.dark["popover"] %>;
    --popover-foreground: <%- colors.dark["popover-foreground"] %>;
    --primary: <%- colors.dark["primary"] %>;
    --primary-foreground: <%- colors.dark["primary-foreground"] %>;
    --secondary: <%- colors.dark["secondary"] %>;
    --secondary-foreground: <%- colors.dark["secondary-foreground"] %>;
    --muted: <%- colors.dark["muted"] %>;
    --muted-foreground: <%- colors.dark["muted-foreground"] %>;
    --accent: <%- colors.dark["accent"] %>;
    --accent-foreground: <%- colors.dark["accent-foreground"] %>;
    --destructive: <%- colors.dark["destructive"] %>;
    --destructive-foreground: <%- colors.dark["destructive-foreground"] %>;
    --border: <%- colors.dark["border"] %>;
    --input: <%- colors.dark["input"] %>;
    --ring: <%- colors.dark["ring"] %>;
    --chart-1: <%- colors.dark["chart-1"] %>;
    --chart-2: <%- colors.dark["chart-2"] %>;
    --chart-3: <%- colors.dark["chart-3"] %>;
    --chart-4: <%- colors.dark["chart-4"] %>;
    --chart-5: <%- colors.dark["chart-5"] %>;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}`

  for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) {
    const base: Record<string, any> = {
      inlineColors: {},
      cssVars: {},
    }
    for (const [mode, values] of Object.entries(colorMapping)) {
      base['inlineColors'][mode] = {}
      base['cssVars'][mode] = {}
      for (const [key, value] of Object.entries(values)) {
        if (typeof value === 'string') {
          // Chart colors do not have a 1-to-1 mapping with tailwind colors.
          if (key.startsWith('chart-')) {
            base['cssVars'][mode][key] = value
            continue
          }

          const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`)
          base['inlineColors'][mode][key] = resolvedColor

          const [resolvedBase, scale] = resolvedColor.split('-')
          const color = scale
            ? colorsData[resolvedBase].find(
                (item: any) => item.scale === parseInt(scale),
              )
            : colorsData[resolvedBase]
          if (color) {
            base['cssVars'][mode][key] = color.hslChannel
          }
        }
      }
    }

    const eta = new Eta()
    // Build css vars.
    base['inlineColorsTemplate'] = eta.render(BASE_STYLES, {})
    base['cssVarsTemplate'] = eta.render(BASE_STYLES_WITH_VARIABLES, {
      colors: base['cssVars'],
    })

    await fs.writeFile(
      path.join(REGISTRY_PATH, `colors/${baseColor}.json`),
      JSON.stringify(base, null, 2),
      'utf8',
    )

    // ----------------------------------------------------------------------------
    // Build registry/themes.css
    // ----------------------------------------------------------------------------
    const THEME_STYLES_WITH_VARIABLES = `
.theme-<%- theme %> {
  --background: <%- colors.light["background"] %>;
  --foreground: <%- colors.light["foreground"] %>;

  --muted: <%- colors.light["muted"] %>;
  --muted-foreground: <%- colors.light["muted-foreground"] %>;

  --popover: <%- colors.light["popover"] %>;
  --popover-foreground: <%- colors.light["popover-foreground"] %>;

  --card: <%- colors.light["card"] %>;
  --card-foreground: <%- colors.light["card-foreground"] %>;

  --border: <%- colors.light["border"] %>;
  --input: <%- colors.light["input"] %>;

  --primary: <%- colors.light["primary"] %>;
  --primary-foreground: <%- colors.light["primary-foreground"] %>;

  --secondary: <%- colors.light["secondary"] %>;
  --secondary-foreground: <%- colors.light["secondary-foreground"] %>;

  --accent: <%- colors.light["accent"] %>;
  --accent-foreground: <%- colors.light["accent-foreground"] %>;

  --destructive: <%- colors.light["destructive"] %>;
  --destructive-foreground: <%- colors.light["destructive-foreground"] %>;

  --ring: <%- colors.light["ring"] %>;

  --radius: <%- colors.light["radius"] %>;
}

.dark .theme-<%- theme %> {
  --background: <%- colors.dark["background"] %>;
  --foreground: <%- colors.dark["foreground"] %>;

  --muted: <%- colors.dark["muted"] %>;
  --muted-foreground: <%- colors.dark["muted-foreground"] %>;

  --popover: <%- colors.dark["popover"] %>;
  --popover-foreground: <%- colors.dark["popover-foreground"] %>;

  --card: <%- colors.dark["card"] %>;
  --card-foreground: <%- colors.dark["card-foreground"] %>;

  --border: <%- colors.dark["border"] %>;
  --input: <%- colors.dark["input"] %>;

  --primary: <%- colors.dark["primary"] %>;
  --primary-foreground: <%- colors.dark["primary-foreground"] %>;

  --secondary: <%- colors.dark["secondary"] %>;
  --secondary-foreground: <%- colors.dark["secondary-foreground"] %>;

  --accent: <%- colors.dark["accent"] %>;
  --accent-foreground: <%- colors.dark["accent-foreground"] %>;

  --destructive: <%- colors.dark["destructive"] %>;
  --destructive-foreground: <%- colors.dark["destructive-foreground"] %>;

  --ring: <%- colors.dark["ring"] %>;
}`

    const themeCSS = []
    for (const theme of baseColors) {
      themeCSS.push(
        // @ts-ignore
        template(THEME_STYLES_WITH_VARIABLES)({
          colors: theme.cssVars,
          theme: theme.name,
        }),
      )
    }

    await fs.writeFile(
      path.join(REGISTRY_PATH, `themes.css`),
      themeCSS.join('\n'),
      'utf8',
    )

    // ----------------------------------------------------------------------------
    // Build registry/themes/[theme].json
    // ----------------------------------------------------------------------------
    rimraf.sync(path.join(REGISTRY_PATH, 'themes'))
    for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) {
      const payload: Record<string, any> = {
        name: baseColor,
        label: baseColor.charAt(0).toUpperCase() + baseColor.slice(1),
        cssVars: {},
      }
      for (const [mode, values] of Object.entries(colorMapping)) {
        payload.cssVars[mode] = {}
        for (const [key, value] of Object.entries(values)) {
          if (typeof value === 'string') {
            const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`)
            payload.cssVars[mode][key] = resolvedColor

            const [resolvedBase, scale] = resolvedColor.split('-')
            const color = scale
              ? colorsData[resolvedBase].find(
                  (item: any) => item.scale === parseInt(scale),
                )
              : colorsData[resolvedBase]
            if (color) {
              payload['cssVars'][mode][key] = color.hslChannel
            }
          }
        }
      }

      const targetPath = path.join(REGISTRY_PATH, 'themes')

      // Create directory if it doesn't exist.
      if (!existsSync(targetPath)) {
        await fs.mkdir(targetPath, { recursive: true })
      }

      await fs.writeFile(
        path.join(targetPath, `${payload.name}.json`),
        JSON.stringify(payload, null, 2),
        'utf8',
      )
    }
  }
}

try {
  const result = registrySchema.safeParse(registry)

  if (!result.success) {
    console.error(result.error)
    process.exit(1)
  }

  await buildRegistry(result.data)
  await buildStyles(result.data)
  await buildStylesIndex()
  // await buildThemes()

  await buildRegistryIcons()
  await buildIconsRegistry()
  await buildIcons()

  console.log('✅ Done!')
} catch (error) {
  console.error(error)
  process.exit(1)
}
